if (window.google) {
	google.load("feeds", "1");
}

onAppReady(function() {
	
	var msg = modernizr([
		'indexeddb',
		'rgba',
		'cssanimations', 'csstransforms'
	]);
	if(msg.length > 0){
		alert(msg.join('\n'));
		return;
	}

	var indexedDB = window.indexedDB || window.webkitIndexedDB
			|| window.mozIndexedDB || window.moz_indexedDB
			|| window.msIndexedDB;
	var IDBCursor = window.IDBCursor || window.webkitIDBCursor
			|| window.mozIDBCursor || window.moz_IDBCursor
			|| window.msIDBCursor;
	var IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction
			|| window.mozIDBTransaction || window.moz_IDBTransaction
			|| window.msIDBTransaction;
	var IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange
			|| window.mozIDBKeyRange || window.moz_IDBKeyRange
			|| window.msIDBKeyRange;
	
	// Database Object
	var db;
	var feedEntriesList = $('#feedEntriesList');
	
	// DB
	var DB_NAME = "offline-feed-reader93";
	var FEED_STORE = "Feed";
	var FEED_ENTRY_STORE = "FeedEntry";

	var ITEMS_PER_PAGE = 50;

	var MARK_OFF = "images/icon_star1.png";
	var MARK_ON = "images/icon_star2.png";

	var shouldInitData = false;
	var setSyncing;
	var feedContentViewerDisplayed;
	
	(function() {
		var syncing = false;
		function setStatusIcon(status) {
			$('.statusIcon').hide();
			$('#statusIcon_' + status).attr('title', status).show();
		}
		function onOnline() {
			if (syncing) {
				return;
			}
			setStatusIcon('online');
		}
		function onOffline() {
			if (syncing) {
				return;
			}
			setStatusIcon('offline');
		}

		$(window).on('online', onOnline).on('offline', onOffline);
		navigator.onLine ? onOnline() : onOffline();

		setSyncing = function(b, options) {
			syncing = b;
			if (syncing) {
				setStatusIcon('syncing');
				var prog = $('#syncProgress');
				var progElem = prog[0]
				if (options) {
					if (options.progress) {
						progElem.value = Math.floor(progElem.max
								* options.progress);
						prog.text(progElem.value + '%');
					}
					if (options.feedTotal) {
						$('#feedTotal').text(options.feedTotal);
					}
					if (options.currentFeedUrl) {
						$('#currentFeedUrl').text(options.currentFeedUrl);
					}
				}
			} else {
				navigator.onLine ? onOnline() : onOffline();
			}
		};
	})();
	// オブジェクトストアを再作成する
	function recreateObjectStore(db, storeName, createOpts) {
		var storeNames = db.objectStoreNames;
		// オブジェクトストアが存在すれば削除
		for ( var i = 0, n = storeNames.length; i < n; i++) {
			if (storeNames[i] === storeName) {
				db.deleteObjectStore(storeName);
			}
		}
		var store = db.createObjectStore(storeName, createOpts);
		return store;
	}
	function initData(callback) {
		var INITIAL_FEED_URLS = [
		                         'http://blogs.msdn.com/ie/rss.xml',
		                         'http://feeds.feedburner.com/html5rocks',
		                         'http://my.opera.com/desktopteam/xml/rss/blog/',
		                         'http://blog.chromium.org/feeds/posts/default',
		                         'http://chrome.blogspot.com/feeds/posts/default',
		                         'http://developer.mozilla.org/devnews/index.php/feed/atom/',
		                         'http://mozillalabs.com/feed/',
		                         'http://feeds2.feedburner.com/html5doctor',
		                         'http://www.quirksmode.org/blog/atom.xml',
		                         'http://blog.whatwg.org/feed',
		                         'http://www.w3.org/QA/atom.xml',
		                         'http://www.w3.org/News/atom.xml'
		                         ];
		var putSucceeded = 0;
		for (var i = 0, n = INITIAL_FEED_URLS.length; i < n; i++) {
			try {
				var tx = db.transaction([FEED_STORE], IDBTransaction.READ_WRITE);
				var feedStore = tx.objectStore(FEED_STORE);
				var putReq = feedStore.put({
					feedUrl: INITIAL_FEED_URLS[i]
				});
				putReq.onsuccess = function(e) {
					putSucceeded++;
					if (putSucceeded === n) {
						callback();
					}
				};
				putReq.onerror = idbError;
			} catch (e) {
				var s = "";
				for (var p in e) {
					s += p + ":" + e[p] + "\n";
				}
				console.log(s);
				alert('Database error occured. You cannot use this application.');
				throw e;
			}
		}
	}
	// DBのマイグレーション操作を保持する
	var DB_MIGRATION_OPS = [
	// Version 0
	function(db, onSuccess, onError) {
		// URLをキーとしてエントリを保存するオブジェクトストアを作成
		var feedStore = recreateObjectStore(db, FEED_STORE, {
			keyPath : "feedUrl"
		});
		feedStore.createIndex("date", "date");
		
		// 
		var feedEntryStore = recreateObjectStore(db, FEED_ENTRY_STORE, {
			keyPath : "link"
		});
		feedEntryStore.createIndex("date", "date");
		feedEntryStore.createIndex("feedUrl", "feedUrl");
		shouldInitData = true;
		onSuccess(db);
	} ];

	function openIndexedDB(callback) {
		var latestVersion = DB_MIGRATION_OPS.length;
		// 非同期でDBをオープン
		var req = indexedDB.open(DB_NAME, latestVersion);
		function upgrade(currentVersion, latestVersion, cb) {
			var migrate = function(i) {
				DB_MIGRATION_OPS[i](db, function() {
					i++;
					if (i === latestVersion) {
						// migration finished!
						typeof cb == "function" && cb();
						return;
					}
					migrate(i);
				});
			};
			migrate(currentVersion);
		}
		req.onupgradeneeded = function(event) {
			db = req.result;
			var currentVersion = event.oldVersion;
			// 現在のバージョンと最新バージョンの間に差異があったら、
			// マイグレーション操作を順に実行
			if (currentVersion != latestVersion) {
				// 順に呼び出し
				upgrade(currentVersion, latestVersion, callback);
			} else {
				typeof callback == "function" && callback();
			}
		};
		req.onsuccess = function(event) {
			db = req.result;
			if (typeof db.setVersion === 'function') {
				var currentVersion = db.version || 0;
				var latestVersion = DB_MIGRATION_OPS.length;
				// 現在のバージョンと最新バージョンの間に差異があったら、
				// マイグレーション操作を順に実行
				if (currentVersion != latestVersion) {
					// バージョンをセットして
					db.setVersion(latestVersion).onsuccess = function(event) {
						// 順に呼び出し
						upgrade(currentVersion, latestVersion, callback);
					};
				} else {
					// database opened successfully
					typeof callback == "function" && callback();
				}
			} else {
				// database opened successfully
				typeof callback == "function" && callback();
			}
		};
		req.onerror = idbError;
	}

	function setEntriesSectionTitle(title) {
		$('#feedEntriesSection h1:first-of-type').text(title);
	}
	
	function fetchFeed(feedUrl, onSuccess, onError) {
		var feed = new google.feeds.Feed(feedUrl);
		feed.setNumEntries(10);
		feed.load(function(result) {
			if (result.error) {
				typeof onError === 'function' && onError(result.error);
				return;
			}
			typeof onSuccess === 'function' && onSuccess(result.feed);
		});
	}
	function storeFeed(feed, onSuccess, onError) {
		feed.star = false;
		feed.read = false;
		var entries = feed.entries;
		delete feed.entries;
		
		var tx = db.transaction(FEED_STORE, IDBTransaction.READ_WRITE);
		var feedStore = tx.objectStore(FEED_STORE);
		var feedGetReq = feedStore.get(feed.feedUrl);
		feedGetReq.onsuccess = function() {
			var prevData = feedGetReq.result;
			if (prevData) {
				feed.star = prevData.star;
				feed.read = prevData.read;
			}
			feedStore.put(feed);
		};
		feedGetReq.onerror = idbError;
		// ===== Store feed entries
		var tx = db.transaction(FEED_ENTRY_STORE,
				IDBTransaction.READ_WRITE);
		var feedEntryStore = tx.objectStore(FEED_ENTRY_STORE);
		var processedEntryCount = 0, entryCount = entries.length;
		for (var i = 0; i < entryCount; i++) {
			var entry = entries[i];
			entry.feedUrl = feed.feedUrl;
			entry.feedTitle = feed.title;
			entry.star = false;
			entry.read = false;
			var pdate = new Date(entry.publishedDate);
			entry.date = pdate.getTime();
			(function putIfAbsent(entry) {
				var　feedEntryGetReq = feedEntryStore.get(entry.link);
				feedEntryGetReq.onsuccess = function(e) {
					var prevData = e.target.result;
					if (prevData) {
						entry.star = prevData.star;
						entry.read = prevData.read;
					}
					// save entry
					var putReq = feedEntryStore.put(entry);
					putReq.onsuccess = function() {
						processedEntryCount++;
						// all feeds are stored
						if (processedEntryCount === entryCount) {
							typeof onSuccess == "function" && onSuccess(feed, entries);
						}
					};
				};
				feedEntryGetReq.onerror = idbError;
			})(entry);
		}
		
	}
	function fetchAndStoreFeed(feedUrl, onSuccess, onError) {
		fetchFeed(feedUrl, function(feed) {
			storeFeed(feed, function(feed, entries) {
				typeof onSuccess === 'function' && onSuccess(feed, entries);
			}, onError)
		}, onError);
	}
	function fetchAndStoreFeedsAll(feedUrls, callback) {
		var processedFeedCount = 0;
		setSyncing(true, {
			feedTotal : feedUrls.length
		});
		var processedFeedCount = 0;
		for ( var i = 0, n = feedUrls.length; i < n; i++) {
			fetchAndStoreFeed(feedUrls[i], function() {
				processedFeedCount++;
				setSyncing(true, {
					progress : processedFeedCount / n
				});
				if (processedFeedCount === n) {
					callback();
				}
			});
		}
	}
	function searchFeeds(callback) {
		var results = [];
		// このサンプルでは、全部のフィードをIDBから読み出す
		var tx = db.transaction(FEED_STORE, IDBTransaction.READ_ONLY);
		var feedEntryStore = tx.objectStore(FEED_STORE);
		var curReq = feedEntryStore.openCursor();
		curReq.onsuccess = function() {
			var cursor = curReq.result;
			// maybe end of cursor
			if (!cursor) {
				typeof callback == "function" && callback(results);
				return;
			}
			var entry = cursor.value;
			results.push(entry);
			cursor.continue();
		};
	}
	$('#feedEntriesSectionContent').scroll(function() {
		var content = $(this);
		if (this.scrollHeight - this.scrollTop - content.outerHeight() < 20 && EntryLoader.hasNextPage)  {
			EntryLoader.nextPage();
		}
	});
	
	var entrySearchFilter = alwaysTrue;
	var starredEntrySearchFilter = function(entry) {
		return !!entry.star;
	};
	var unreadEntrySearchFilter = function(entry) {
		return !entry.read;
	};
	var feedUrlEntrySearchFilterGenerator = function(feedUrl) {
		return function(entry) {
			return entry.feedUrl === feedUrl;
		};
	};
	function noop() {}
	function alwaysTrue() { return true; }
	
	var EntryLoader = {
		filter: alwaysTrue,
		hasNextPage: false,
		_searchBaseTime: new Date(),
		_pageLoading: false,
		_searchLoadedUrl: [],
		_searchLoadedUrlex: [],
		firstPage: function() {
			this.hasPrevPage = false;
			this.hasNextPage = false;
			this._searchBaseTime = new Date();
			this._searchLoadedUrl = [];
			feedEntriesList.empty();
			this.nextPage();
		},
		nextPage: function() {
			var self = this;
			if (self._pageLoading) {
				return;
			}
			self._pageLoading = true;
			var count = 0;
			var direction = 'next';
			var entries = [];
			self._iterateEntries(function(entry, iterator) {
				count++;
				if (!entry || count === ITEMS_PER_PAGE) {
					self._searchLoadedUrl = self._searchLoadedUrlex;
					self._searchLoadedUrl.pop();
					self._searchLoadedUrlex = [];
					for (var i = 0, n = entries.length; i < n; i++) {
						addEntryRow(entries[i]);
					}
					if (entry) {
						self.hasNextPage = true;
						self._searchBaseTime = entries[n - 1].date;
					} else {
						self.hasNextPage = false;
					}
					//self._updatePageControl();
					self._pageLoading = false;
					return;
				}
				entries.push(entry);
				iterator();
			}, entrySearchFilter);
		},
		_iterateEntries: function(callback) {
			var self = this;
			var baseTime = self._searchBaseTime instanceof Date ? self._searchBaseTime.getTime() : self._searchBaseTime;
			
			var range = IDBKeyRange.upperBound(baseTime, false);
//			var range = IDBKeyRange.upperBound(baseTime, true);
			var tx = db.transaction(FEED_ENTRY_STORE, IDBTransaction.READ_ONLY);
			var feedEntryStore = tx.objectStore(FEED_ENTRY_STORE);
			var curReq = feedEntryStore.index("date").openCursor(range, IDBCursor.PREV);
			curReq.onsuccess = function() {
				var cursor = curReq.result;
				if (!cursor) {
					callback(undefined);
					return;
				}
				var entry = cursor.value;
				if ($.inArray(entry.link, self._searchLoadedUrl) !== -1) {
					cursor.continue();
				} else
				if (self.filter(entry)) {
					self._searchLoadedUrlex.push(entry.link);
					callback(entry, function() {
						cursor.continue();
					});
				} else {
					cursor.continue();
				}
			};
			curReq.onerror = idbError;
		}
	};
	
	function addFeedRow(feed) {
		var li = $("<li/>").data("feed", feed);
		var feedLink = $("<a/>", {
			'class': 'feedLink',
			href: 'javascript:void(0);',
			text: feed.title,
			click: function() {
				$('#closeFeedContentViewerButton').click();
				var feed = $(this).closest('li').data('feed');
				EntryLoader.filter = feedUrlEntrySearchFilterGenerator(feed.feedUrl);
				EntryLoader.firstPage();
				setEntriesSectionTitle(feed.title);
				return false;
			}
		});
		var delButton = $("<div class='unregisterFeedButton'/>");
		delButton.click(function() {
			var feed = $(this).closest('li').data('feed');
			if (!confirm(msg_confirmUnregisterFeed)) {
				return;
			}
			// Delete feed and entries from database
			var tx = db.transaction([FEED_STORE, FEED_ENTRY_STORE], IDBTransaction.READ_WRITE);
			var feedStore = tx.objectStore(FEED_STORE);
			var feedEntryStore = tx.objectStore(FEED_ENTRY_STORE);
			var req = feedStore.delete(feed.feedUrl);
			req.onsuccess = function(e) {
				var index = feedEntryStore.index("feedUrl");
				var curReq = index.openCursor(IDBKeyRange.only(feed.feedUrl));
				curReq.onsuccess = function() {
					var cursor = curReq.result;
					// maybe end of cursor
					if (!cursor) {
						refreshFeeds();
						EntryLoader.filter = alwaysTrue;
						EntryLoader.firstPage();
						return;
					}
					var entry = cursor.value;
					feedEntryStore.delete(entry.link);
					console.log("deleted:" + entry.link);
					cursor.continue();
				};
				curReq.onerror = idbError;
			};
			req.onerror = idbError;
		});
		li.append(feedLink).append(delButton).appendTo($("#feedList"));
		return li;
	}
	function refreshFeeds() {
		// search feed entry from IDB
		searchFeeds(function(feeds) {
			// show list
			$("#feedList").empty();
			for (var i = 0, n = feeds.length; i < n; i++) {
				var feed = feeds[i];
				addFeedRow(feed);
			}
		});
		
	}
	// Open IDB
	openIndexedDB(function() {
		if (shouldInitData) {
			setTimeout(function() {
				initData(function() {
					saveFeedEntry();
				});
			}, 1000);
		} else {
			saveFeedEntry();
		}
	});
	function saveFeedEntry() {
		var loadFeedEntriesFromLocal = function() {
			// search feed entry from IDB
			setEntriesSectionTitle(msg_allItems);
			EntryLoader.firstPage();
		};
		refreshFeeds();
		loadFeedEntriesFromLocal();
		// Entries
		if (window.google) {
			setSyncing(true);
			searchFeeds(function(feeds) {
				var feedUrls = feeds.map(function(feed) {
					return feed.feedUrl;
				});
				fetchAndStoreFeedsAll(feedUrls, function() {
					loadFeedEntriesFromLocal();
					setSyncing(false);
					setTimeout(function() {
						$('#syncDetail').fadeOut();
					}, 1000);
				});
			});
		} else {
			loadFeedEntriesFromLocal();
			setSyncing(false);
			$('#syncDetail').hide();
		}
	}
	function addEntryRow(entry) {
		// entryEntry
		var starMark = entry.star ? MARK_ON : MARK_OFF;
		
		var row = $("<tr/>", {id: 'read_' + entry.link});
		row.data("entry", entry);
		
		// read
		if (entry.read) {
			row.attr('style', 'background-color: #e2cfc3;color:#7d7d7d;');
		}
		
		// star
		var starCell = $('<td class="starCell"/>').appendTo(row);
		$('<img/>', {
			id: 'star_' + entry.link,
			src: starMark,
			data: { 'entryUrl': entry.link },
			click: function() {
				var entryUrl = $(this).data('entryUrl');
				changeFeedEntryStar(entryUrl);
			}
		}).appendTo(starCell);

		// feed title
		$('<td/>', {
			'class': 'feedTitle',
			html: entry.feedTitle
		}).appendTo(row);

		// title
		$('<td/>', {
			'class': 'entryTitle',
			style: 'margin:0 5px;',
			text: entry.title,
			click: function() {
				var entry = row.data("entry");
				$('#feedContentViewer').find('h3 > a').attr("href", entry.link).text(entry.title)
				.end().find('.resource').html(entry.feedTitle)
				.end().find('.date').html(formatDateMillis(entry.date))
				.end().find('.link').attr('href', entry.link)
				.end().find('.content').html(entry.content)
				.end().fadeIn(300, function() {
					feedContentViewerDisplayed = true;
				});
				changeFeedEntryRead(entry.link);
			}
		}).appendTo(row);
		
		// timestamp
		$('<td/>', {
			'class': 'feedDateTime',
			text: formatDateMillis(entry.date)
		}).appendTo(row);

		feedEntriesList.append(row);
	}
	
	$('#msg_allItems').click(function() {
		setEntriesSectionTitle(msg_allItems);
		EntryLoader.filter = alwaysTrue;
		EntryLoader.firstPage();
		return false;
	});
	$('#msg_starItems').click(function() {
		setEntriesSectionTitle(msg_starItems);
		EntryLoader.filter = starredEntrySearchFilter;
		EntryLoader.firstPage();
		return false;
	});
	$('#msg_unreadItems').click(function() {
		setEntriesSectionTitle(msg_unreadItems);
		EntryLoader.filter = unreadEntrySearchFilter;
		EntryLoader.firstPage();
		return false;
	});
	function formatDateMillis(millis) {
		var pdate = new Date();
		pdate.setTime(millis);
		var pday = pdate.getDate();
		var pmonth = pdate.getMonth() + 1;
		var pyear = pdate.getFullYear();
		return pyear + '-' + ("0" + pmonth).slice(-2) + '-'
			+ ("0" + pday).slice(-2) + ' ' + pdate.toLocaleTimeString();
	}
	$(function() {
		$('#addFeedForm').submit(function() {
			var feedUrl = $('#newFeedUrl').val();
			var tx = db.transaction([FEED_STORE], IDBTransaction.READ_WRITE);
			var feedStore = tx.objectStore(FEED_STORE);
			var getReq = feedStore.get(feedUrl);
			getReq.onsuccess = function(e) {
				var prevFeed = e.target.result;
				if (prevFeed) {
					alert(msg_feedAlreadyExists)
					return;
				}
				fetchFeed(feedUrl, function(feed) {
					// Show preview of feed entries
					feedEntriesList.empty();
					var entries = feed.entries;
					for (var i = 0, n = Math.min(entries.length, ITEMS_PER_PAGE); i < n; i++) {
						var entry = entries[i];
						entry.feedTitle = feed.title;
						var pdate = new Date(entry.publishedDate);
						entry.date = pdate.getTime();
						addEntryRow(entry);
					}
					setEntriesSectionTitle(feed.title);
					if (confirm(msg_confirmAddFeed)) {
						storeFeed(feed, function() {
							var row = addFeedRow(feed);
							row.find('.feedLink').click();
							$('#newFeedUrl').val('');
						});
					}
				}, function() {
					alert(msg_failedToFetchFeed);
				});
			};
			return false;
		});
	});
	
	// i18n
	replaceI18N([
		"msg_url",
		"msg_title",
		
		"msg_regist",
		"msg_registExplain",
		"msg_allItems",
		"msg_starItems",
		"msg_unreadItems",
		"msg_feeds",
		"msg_add",
		"msg_visitToSourceSite"
	 ]);
	
	function replaceI18N(names) {
		for(var i = 0; i < names.length; i ++) {
			var msgKey = names[i];
			$('#' + msgKey).text(window[msgKey]);
		}
	}
	function changeFeedEntryStar(url) {
		if (!db) {
			return;
		}
		if (!db.objectStoreNames.contains(FEED_ENTRY_STORE)) {
			return;
		}
	
		// Create a transaction that locks the world.
		var objectStore = db.transaction([FEED_ENTRY_STORE], IDBTransaction.READ_WRITE)
			.objectStore(FEED_ENTRY_STORE);
		var req = objectStore.get(url);
		req.onsuccess = function(e) {
			var entry = req.result;
			entry.star = !entry.star;

			var request = objectStore.put(entry);
			request.onerror = console.log;
			request.onsuccess = function() {
				document.getElementById('star_'+entry.url).src = entry.star ? MARK_ON : MARK_OFF;;
			};
		};
	}
	function changeFeedEntryRead(url) {
		if (!db) {
			return;
		}
		if (!db.objectStoreNames.contains(FEED_ENTRY_STORE)) {
			return;
		}
	
		// Create a transaction that locks the world.
		var objectStore = db.transaction([FEED_ENTRY_STORE], IDBTransaction.READ_WRITE)
			.objectStore(FEED_ENTRY_STORE);
		var req = objectStore.get(url);
		req.onsuccess = function(e) {
			var entry = req.result;
			
			var storedata = entry;
			storedata.url = url;
			storedata.read = true;
			
			var request = objectStore.put(storedata);
			request.onerror = console.log;
			request.onsuccess = function() {
				document.getElementById('read_'+storedata.url).style.backgroundColor = '#e2cfc3';
			};
		};
	}
	function idbError(e) {
		var req = e.target;
		console.log(req.error);
		alert('Database error occured');
	}
	$('#closeFeedContentViewerButton').click(function() {
		$('#feedContentViewer').fadeOut(function() {
			feedContentViewerDisplayed = false;
		});
	});
	$(document.body).click(function(e) {
		if (!feedContentViewerDisplayed) {
			return;
		}
		var srcElem = $(e.target);
		if (srcElem.is('#feedContentViewer') || srcElem.parents('#feedContentViewer').length > 0) {
			return;
		}
		$('#closeFeedContentViewerButton').click();
	});
});
